package fr.castorflex.openharmony.circularprogressbar;

import ohos.agp.animation.Animator;
import ohos.agp.components.Component;
import ohos.agp.components.element.FrameAnimationElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.powermanager.PowerManager;
import org.jetbrains.annotations.NotNull;

public class CircularProgressDrawable extends FrameAnimationElement {
    private final RectFloat fBounds = new RectFloat();

    public interface OnEndListener {
        void onEnd(CircularProgressDrawable drawable);
    }

    public static final int STYLE_ROUNDED = 1;
    private final PowerManager mPowerManager;
    private final Options mOptions;
    private final Paint mPaint;
    private boolean mRunning;
    private PBDelegate mPBDelegate;
    private float mBorderWidth;
    private Component component;

    /**
     * Private method, use #Builder instead
     */
    private CircularProgressDrawable(PowerManager powerManager, Options options) {
        mOptions = options;
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE_STYLE);
        mBorderWidth = options.borderWidth;
        mPaint.setStrokeWidth(mBorderWidth);
        mPaint.setStrokeCap(options.style == STYLE_ROUNDED ? Paint.StrokeCap.ROUND_CAP : Paint.StrokeCap.BUTT_CAP);
        mPaint.setColor(Color.RED);
        mPowerManager = powerManager;

        initDelegate();
    }

    public void setComponent(Component component) {
        if (component != null) {
            this.component = component;
        }
    }

    public void onBoundsChange(Rect bounds) {
        fBounds.left = bounds.left + mBorderWidth / 2f + .5f;
        fBounds.right = bounds.right - mBorderWidth / 2f - .5f;
        fBounds.top = bounds.top + mBorderWidth / 2f + .5f;
        fBounds.bottom = bounds.bottom - mBorderWidth / 2f - .5f;
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void drawToCanvas(@NotNull Canvas canvas) {
        if (isRunning()) mPBDelegate.draw(canvas, mPaint);
    }

    public boolean isRunning() {
        return mRunning;
    }

    @Override
    public void start() {
        initDelegate();
        mPBDelegate.start();
        if (isRunning()) {
            return;
        }

        mRunning = true;
    }

    public void invalidateSelf() {
        if (component != null) component.invalidate();
    }

    /**
     * Inits the delegate. Create one if the delegate is null or not the right mode
     */
    private void initDelegate() {
        boolean powerSaveMode = Utils.isPowerSaveModeEnabled(mPowerManager);
        if (powerSaveMode) {
            if (mPBDelegate == null || !(mPBDelegate instanceof PowerSaveModeDelegate)) {
                if (mPBDelegate != null) mPBDelegate.stop();
                mPBDelegate = new PowerSaveModeDelegate(this);
            }
        } else {
            if (mPBDelegate == null || (mPBDelegate instanceof PowerSaveModeDelegate)) {
                if (mPBDelegate != null) mPBDelegate.stop();
                mPBDelegate = new DefaultDelegate(this, mOptions);
            }
        }
    }

    public void setStrokeWidth(float strokeWidth) {
        if (strokeWidth < 0) throw new IllegalArgumentException("The strokeWidth must be >= 0");
        mPaint.setStrokeWidth(strokeWidth);
        invalidateSelf();
    }

    public void setSpeed(float speed) {
        if (speed < 0) throw new IllegalArgumentException("Speed must be >= 0");
        mPBDelegate.setSpeed(speed);
        invalidateSelf();
    }

    public void setRotation(float speed) {
        if (speed < 0) throw new IllegalArgumentException("Speed must be >= 0");
        mPBDelegate.setRotation(speed);
        invalidateSelf();
    }

    @Override
    public void stop() {
        if (!isRunning()) {
            return;
        }
        mRunning = false;
        mRunning = false;
        mPBDelegate.stop();
    }

    Paint getCurrentPaint() {
        return mPaint;
    }

    RectFloat getDrawableBounds() {
        return fBounds;
    }

    ////////////////////////////////////////////////////////////////////
    //Progressive stop
    ////////////////////////////////////////////////////////////////////

    public void progressiveStop(OnEndListener listener) {
        mPBDelegate.progressiveStop(listener);
    }

    public void progressiveStop() {
        progressiveStop(null);
    }

    ////////////////////////////////////////////////////////////////////
    //Builder
    ////////////////////////////////////////////////////////////////////
    public static class Builder {
        private static final int DEFAULT_ROTATION_CURVETYPE = Animator.CurveType.LINEAR;
        private float mBorderWidth;
        private int[] mColors;
        private float mSweepSpeed;
        private float mRotationSpeed;
        private int mMinSweepAngle;
        private int mMaxSweepAngle;
        int mStyle;
        private PowerManager mPowerManager;

        public Builder(@NotNull Context context) {
            this(context, false);
        }

        public Builder(@NotNull Context context, boolean editMode) {
            initValues(context, editMode);
        }

        private void initValues(@NotNull Context context, boolean editMode) {
            mSweepSpeed = 1f;
            mRotationSpeed = 1f;
            if (editMode) {
                mMinSweepAngle = 20;
                mMaxSweepAngle = 300;
            } else {
                mMinSweepAngle = 20;
                mMaxSweepAngle = 300;
            }
            mStyle = CircularProgressDrawable.STYLE_ROUNDED;
            mPowerManager = Utils.powerManager(context);
        }

        public Builder color(int color) {
            mColors = new int[]{color};
            return this;
        }

        public Builder colors(int[] colors) {
            Utils.checkColors(colors);
            mColors = colors;
            return this;
        }

        public Builder sweepSpeed(float sweepSpeed) {
            Utils.checkSpeed(sweepSpeed);
            mSweepSpeed = sweepSpeed;
            return this;
        }

        public Builder rotationSpeed(float rotationSpeed) {
            Utils.checkSpeed(rotationSpeed);
            mRotationSpeed = rotationSpeed;
            return this;
        }

        public Builder minSweepAngle(int minSweepAngle) {
            Utils.checkAngle(minSweepAngle);
            mMinSweepAngle = minSweepAngle;
            return this;
        }

        public Builder maxSweepAngle(int maxSweepAngle) {
            Utils.checkAngle(maxSweepAngle);
            mMaxSweepAngle = maxSweepAngle;
            return this;
        }

        public Builder strokeWidth(float strokeWidth) {
            Utils.checkPositiveOrZero(strokeWidth, "StrokeWidth");
            mBorderWidth = strokeWidth;
            return this;
        }

        public Builder style(int style) {
            mStyle = style;
            return this;
        }

        public CircularProgressDrawable build() {
            return new CircularProgressDrawable(
                    mPowerManager, new Options(DEFAULT_ROTATION_CURVETYPE,
                    mBorderWidth,
                    mColors,
                    mSweepSpeed,
                    mRotationSpeed,
                    mMinSweepAngle,
                    mMaxSweepAngle,
                    mStyle));
        }
    }
}